Exercise 3: Modeling Review and Intro to tidymodels
1. a) Without doing any analysis, what are some variables you think might be predictive and why?
Hotel (type), arrival_date_month, booking_changes. Hotel type will be important as individuals traveling for work related purposes I assume will be less likely to cancel reservations, while people on vacation would be more likely to cancel. So I think ‘City Hotel’ will have less cancellations. arrival_date_month might be predictive for the same reason as hotel, trying to figure out who is traveling for work versus pleasure. booking_changes, I assume the higher the changes the less likely someone is to cancel, as the reason they might cancel is less strict because they are booking around it rather than something that comes up that makes it impossible to go.
b) What are some problems that might exist with the data? You might think about how it was collected and who did the collecting.
Redundant variables, ‘is_canceled’ and ‘reservation_status’ for terms of modeling having the extra variable can cause issues. Lack of id’s for each booking is slightly annoying due to possibility compressing multiple reservations into one booking. I am also unsure of how some variables were collected such as previous booking/cancellation data.
c) If we construct a model, what type of conclusions will be able to draw from it?
If families or individuals are more likely to cancel, times of year people are more likely to cancel. I don’t think we will find causation of why things are canceled or not, but we should be able to find some trends of when things are more likely canceled.
2. Create some exploratory plots or table summaries of the data, concentrating most on relationships with the response variable. Keep in mind the response variable is numeric, 0 or 1. You may want to make it categorical (you also may not). Be sure to also examine missing values or other interesting values.
hotels %>%
group_by(previous_cancellations) %>%
summarise(total = n())
hotels %>%
add_n_miss() %>%
filter(n_miss_all != 0) %>%
select(children)
hotels %>%
ggplot(aes(x = customer_type, fill = hotel)) +
geom_bar()

3. Mutations.
hotels_mod <- hotels %>%
mutate(is_canceled = as.factor(is_canceled)) %>%
mutate(across(where(is.character), as.factor)) %>%
select(-arrival_date_year,
-reservation_status,
-reservation_status_date) %>%
add_n_miss() %>%
filter(n_miss_all == 0) %>%
select(-n_miss_all)
set.seed(494)
hotels_split <- initial_split(hotels_mod, prop = .5, strata = 'is_canceled')
hotels_training <- training(hotels_split)
hotels_testing <- testing(hotels_split)
4. Pre-processing
hotels_recipe <- recipe(is_canceled ~ .,
data = hotels_training) %>%
step_mutate(children = as.factor(ifelse(children == 0,0,1)),
babies = as.factor(ifelse(babies == 0,0,1)),
previous_cancellations = as.factor(ifelse(previous_cancellations == 0, 0,1))) %>%
step_mutate(agent = as.factor(ifelse(agent == 'NULL', 0,1)),
company = as.factor(ifelse(company == 'NULL', 0,1))) %>%
step_mutate(country = fct_lump_n(country, n = 5)) %>%
step_normalize(all_predictors(),-all_nominal()) %>%
step_dummy(all_nominal(),
-all_outcomes())
5. Recipe Building.
In general, why would we want to use LASSO instead of regular logistic regression? (HINT: think about what happens to the coefficients).
We can avoid over fitting the model to the test data, by compressing and using less variable outcomes.
hotels_recipe %>%
prep(hotels_training) %>%
juice()
set.seed(494)
hotel_cv <- vfold_cv(hotels_training, v = 5)
hotel_lasso_mod <-
logistic_reg(mixture = 1) %>%
set_args(penalty = tune()) %>%
set_engine("glmnet") %>%
set_mode("classification")
hotel_lasso_wf <-
workflow() %>%
add_recipe(hotels_recipe) %>%
add_model(hotel_lasso_mod)
hotel_lasso_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
##
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 5 Recipe Steps
##
## ● step_mutate()
## ● step_mutate()
## ● step_mutate()
## ● step_normalize()
## ● step_dummy()
##
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
##
## Main Arguments:
## penalty = tune()
## mixture = 1
##
## Computational engine: glmnet
6. Tuning.
penalty_grid <- grid_regular(penalty(), levels = 10)
penalty_grid
hotel_lass_tune <- hotel_lasso_wf %>%
tune_grid(resamples = hotel_cv,
grid = penalty_grid)
hotel_lass_tune
hotel_lass_tune %>%
collect_metrics() %>%
filter(.metric == "accuracy") %>%
ggplot(aes(x = penalty, y = mean)) +
geom_point() +
geom_line() +
scale_x_log10(
breaks = scales::trans_breaks("log10", function(x) 10^x),
labels = scales::trans_format("log10",scales::math_format(10^.x))) +
labs(x = "penalty", y = "accuracy")

hotel_lass_tune %>%
show_best(metric = 'accuracy')
hotel_lass_tune %>%
show_best()
best_param <- hotel_lass_tune %>%
select_best(metric = "accuracy")
best_param
hotel_lasso_final_wf <- hotel_lasso_wf %>%
finalize_workflow(best_param)
hotel_lasso_final_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
##
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 5 Recipe Steps
##
## ● step_mutate()
## ● step_mutate()
## ● step_mutate()
## ● step_normalize()
## ● step_dummy()
##
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
##
## Main Arguments:
## penalty = 1e-10
## mixture = 1
##
## Computational engine: glmnet
hotel_lasso_final_mod <- hotel_lasso_final_wf %>%
fit(data = hotels_training)
hotel_lasso_final_mod %>%
pull_workflow_fit() %>%
tidy()
Are there some variables with coefficients of 0?
Yes. ‘arrival_date_month_September’, ‘assigned_room_type_L’, ‘market_segment_Groups’, ‘market_segment_Undefined’, ‘distribution_channel_Undefined’.
7. Testing and Evaluation
hotel_lasso_final_mod %>%
pull_workflow_fit() %>%
vip()

Which variables show up as the most important? Are you surprised?
‘reserved_room_type_P’, ‘deposit_type_Non.Refund’, ‘assigned_room_type_l’. Why does room type reserved and assigned matter so much?
hotel_lasso_test <- hotel_lasso_final_wf %>%
last_fit(hotels_split)
hotel_lasso_test %>%
collect_metrics()
How do they compare to the cross-validated metrics?
Test accuracy is .002 higher than my CV metrics on average. And ‘roc_auc’ is about .001 higher.
preds <- collect_predictions(hotel_lasso_test)
preds %>%
conf_mat(is_canceled, .pred_class)
## Truth
## Prediction 0 1
## 0 34291 7747
## 1 3292 14363
What is the true positive rate (sensitivity)? What is the true negative rate (specificity)?
True positive rate = 65%. True negative rate = 91%.
preds %>%
ggplot(aes(x=.pred_1, fill = is_canceled)) +
geom_density(alpha = .5, color = NA)

a. What would this graph look like for a model with an accuracy that was close to 1?
There would be very high density peaks at 0 and 1, and very little to no density in the middle.
b. Our predictions are classified as canceled if their predicted probability of canceling is greater than .5. If we wanted to have a high true positive rate, should we make the cutoff for predicted as canceled higher or lower than .5?
Higher cutoff would lead to a higher true positive rate, as the model would only predict cancellations on cases it is very sure of.
c. What happens to the true negative rate if we try to get a higher true positive rate?
The true negative would decrease.
8. Let’s say that this model is going to be applied to bookings 14 days in advance of their arrival at each hotel, and someone who works for the hotel will make a phone call to the person who made the booking. During this phone call, they will try to assure that the person will be keeping their reservation or that they will be canceling in which case they can do that now and still have time to fill the room. How should the hotel go about deciding who to call? How could they measure whether it was worth the effort to do the calling? Can you think of another way they might use the model?
If the hotel is going to call someone, the model should be overwhelmingly certain that the person is likely to cancel. The cutoff would have to be such that the true positive rate is very high, so the hotel isn’t calling people who aren’t likely to cancel. Measure success if the rooms of the people who cancel from that call are getting filled within the 14 days after the call. Determining what travel agents and companies have the most reliable customers, if the contracts they have are worth while for them to keep.
9. How might you go about questioning and evaluating the model in terms of fairness? Are there any questions you would like to ask of the people who collected the data?
Country of origin could be problematic, as well discrimination based on number of children could be an issue. I am also confused on how cancellation and reservation data was collected. If it was based on website booking or travel agents, then those people using those services would be unfairly deemed as less reliable because they have a record. Whereas people who don’t have those online records won’t have the same issues.
Bias and Fairness
Did you hear anything that surprised you?
When responding to the first question from the audience, Dr. Thomas brings up that machine learning has only focused on how to obtain large amounts of data efficiently rather than looking at how the data is obtained and the structure of the data itself. This was an interesting point I hadn’t thought about before, the topic of ensuring test data matches training data. I guess I hadn’t thought about it before because I work with data sets from MLB the most and there isn’t that much difference year to year in outcomes.
Why is it important that we pay attention to bias and fairness when studying data science?
It’s important to pay attention to bias and fairness because that work data scientists do can have large impacts on people’s lives, for example models that predict recidivism send can keep people in jail based on factors that have nothing to do with the individual due to large amounts of bias built into the process. The outcomes of these models can prevent freedom from people. On a smaller scale we have to make sure our model does what we want it do. We want the model to be fair across and points in the data so we must make sure there isn’t a large bias towards a subsection of our data set.
Is there a type of bias Dr. Thomas discussed that was new to you? Can you think about places you have seen these types of biases?
Representation bias. I saw a lot of these types of biases when working over last summer. I was working on predicting account viability, the database had something like 10% that were not viable accounts that I was trying to predict would not be viable. I made a model and was impressed with myself that I got it up to 90 some odd percent accuracy. Until I went back and looked at the confusion matrix and saw that it was predicting almost all accounts to be viable, so it was really bad at predicting non-viable accounts. I eventually got around this by constructing a dataset that was 50-50 viable to non-viable, and quickly saw that my model in actual terms sucked.
LS0tCnRpdGxlOiAnRXhlcmNpc2UgIzEnCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFKQpgYGAKCmBgYHtyIGxpYnJhcmllc30KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeW1vZGVscykKbGlicmFyeShuYW5pYXIpCmxpYnJhcnkodmlwKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKYGBgCgpgYGB7ciB0aGVtZX0KdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkKYGBgCgpgYGB7ciBkYXRhfQpob3RlbHMgPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjAvMjAyMC0wMi0xMS9ob3RlbHMuY3N2JykKYGBgCgojIyBFeGVyY2lzZSAxOiBTZXR0aW5nIFVwIEdpdCBhbmQgR2l0aHViIGluIHJTdHVkaW8KCkNyZWF0ZSBSZXBvc2l0b3J5IGZvciBBc3NpZ25tZW50IDEuIExpbms6IFtodHRwczovL2dpdGh1Yi5jb20vbWljaGFlbGhldGxvL0hvbWV3b3JrXzFfQURSXQoKIyMgRXhlcmNpc2UgMjogQ3JlYXRlIFdlYnNpdGUKCiMjIyBhKSBMaW5rIG9mIFdlYnNpdGUKCltodHRwczovL21pY2hhZWxoZWx0b24ubmV0bGlmeS5hcHAvXQoKIyMjIGIpIFBvZGNhc3QgUmVmbGVjdGlvbgpUaGUgcG9kY2FzdCBob3N0cyB0YWxrZWQgYWJvdXQgdGhlIGltcG9ydGFuY2Ugb2YgaGF2aW5nIHByb2plY3RzIHlvdSBoYXZlIHdvcmtlZCBvbiByZWFkaWx5IGF2YWlsYWJsZSB0byB5b3UgZm9yIHRoaW5ncyBsaWtlIGpvYiBpbnRlcnZpZXdzIGFuZCBuZXR3b3JraW5nLiAgVGhleSB0YWxrZWQgYWJvdXQgdGhlIGltcG9ydGFuY2Ugb2YgaGF2aW5nIGV4cGllcmVuY2VzIHRvIGJyaW5nIHVwIGlmIGludGVydmlld2VycyB0YWxrIGFib3V0IHN0cnVnZ2xlcyBvciBicmVha3Rocm91Z2hzIHlvdSd2ZSBoYWQgb24gcHJvamVjdHMgaW4gdGhlIGZpZWxkLiBDcmVhdGluZyBhIHdlYnNpdGUgdG8gaG91c2UgbXkgb2xkLCBuZXcgYW5kIGN1cnJlbnQgcHJvamVjdHMgd2lsbCBiZSBhIGdvb2QgcGxhY2UgdG8gc3RvcmUgdGhlbSBmb3IgdGhlIGZ1dHVyZSB3aGVuIEkgYW0gbG9va2luZyB0byBhcHBseSB0byBqb2JzLCBhbmQgY2FuIGhhdmUgdGhpcyB3ZWJzaXRlIGFzIGEgcmVzdW1lIG9mIHNvcnRzIHRvIHNob3cgb2ZmLgoKIyMgRXhlcmNpc2UgMzogTW9kZWxpbmcgUmV2aWV3IGFuZCBJbnRybyB0byB0aWR5bW9kZWxzCgojIyMgMS4gYSkgV2l0aG91dCBkb2luZyBhbnkgYW5hbHlzaXMsIHdoYXQgYXJlIHNvbWUgdmFyaWFibGVzIHlvdSB0aGluayBtaWdodCBiZSBwcmVkaWN0aXZlIGFuZCB3aHk/CkhvdGVsICh0eXBlKSwgYXJyaXZhbF9kYXRlX21vbnRoLCBib29raW5nX2NoYW5nZXMuICBIb3RlbCB0eXBlIHdpbGwgYmUgaW1wb3J0YW50IGFzIGluZGl2aWR1YWxzIHRyYXZlbGluZyBmb3Igd29yayByZWxhdGVkIHB1cnBvc2VzIEkgYXNzdW1lIHdpbGwgYmUgbGVzcyBsaWtlbHkgdG8gY2FuY2VsIHJlc2VydmF0aW9ucywgd2hpbGUgcGVvcGxlIG9uIHZhY2F0aW9uIHdvdWxkIGJlIG1vcmUgbGlrZWx5IHRvIGNhbmNlbC4gIFNvIEkgdGhpbmsgJ0NpdHkgSG90ZWwnIHdpbGwgaGF2ZSBsZXNzIGNhbmNlbGxhdGlvbnMuIGFycml2YWxfZGF0ZV9tb250aCBtaWdodCBiZSBwcmVkaWN0aXZlIGZvciB0aGUgc2FtZSByZWFzb24gYXMgaG90ZWwsIHRyeWluZyB0byBmaWd1cmUgb3V0IHdobyBpcyB0cmF2ZWxpbmcgZm9yIHdvcmsgdmVyc3VzIHBsZWFzdXJlLiAgYm9va2luZ19jaGFuZ2VzLCBJIGFzc3VtZSB0aGUgaGlnaGVyIHRoZSBjaGFuZ2VzIHRoZSBsZXNzIGxpa2VseSBzb21lb25lIGlzIHRvIGNhbmNlbCwgYXMgdGhlIHJlYXNvbiB0aGV5IG1pZ2h0IGNhbmNlbCBpcyBsZXNzIHN0cmljdCBiZWNhdXNlIHRoZXkgYXJlIGJvb2tpbmcgYXJvdW5kIGl0IHJhdGhlciB0aGFuIHNvbWV0aGluZyB0aGF0IGNvbWVzIHVwIHRoYXQgbWFrZXMgaXQgaW1wb3NzaWJsZSB0byBnby4KCiMjIyBiKSBXaGF0IGFyZSBzb21lIHByb2JsZW1zIHRoYXQgbWlnaHQgZXhpc3Qgd2l0aCB0aGUgZGF0YT8gWW91IG1pZ2h0IHRoaW5rIGFib3V0IGhvdyBpdCB3YXMgY29sbGVjdGVkIGFuZCB3aG8gZGlkIHRoZSBjb2xsZWN0aW5nLgpSZWR1bmRhbnQgdmFyaWFibGVzLCAnaXNfY2FuY2VsZWQnIGFuZCAncmVzZXJ2YXRpb25fc3RhdHVzJyBmb3IgdGVybXMgb2YgbW9kZWxpbmcgaGF2aW5nIHRoZSBleHRyYSB2YXJpYWJsZSBjYW4gY2F1c2UgaXNzdWVzLiAgTGFjayBvZiBpZCdzIGZvciBlYWNoIGJvb2tpbmcgaXMgc2xpZ2h0bHkgYW5ub3lpbmcgZHVlIHRvIHBvc3NpYmlsaXR5IGNvbXByZXNzaW5nIG11bHRpcGxlIHJlc2VydmF0aW9ucyBpbnRvIG9uZSBib29raW5nLiAgSSBhbSBhbHNvIHVuc3VyZSBvZiBob3cgc29tZSB2YXJpYWJsZXMgd2VyZSBjb2xsZWN0ZWQgc3VjaCBhcyBwcmV2aW91cyBib29raW5nL2NhbmNlbGxhdGlvbiBkYXRhLiAKCiMjIyBjKSBJZiB3ZSBjb25zdHJ1Y3QgYSBtb2RlbCwgd2hhdCB0eXBlIG9mIGNvbmNsdXNpb25zIHdpbGwgYmUgYWJsZSB0byBkcmF3IGZyb20gaXQ/IApJZiBmYW1pbGllcyBvciBpbmRpdmlkdWFscyBhcmUgbW9yZSBsaWtlbHkgdG8gY2FuY2VsLCB0aW1lcyBvZiB5ZWFyIHBlb3BsZSBhcmUgbW9yZSBsaWtlbHkgdG8gY2FuY2VsLiAgSSBkb24ndCB0aGluayB3ZSB3aWxsIGZpbmQgY2F1c2F0aW9uIG9mIHdoeSB0aGluZ3MgYXJlIGNhbmNlbGVkIG9yIG5vdCwgYnV0IHdlIHNob3VsZCBiZSBhYmxlIHRvIGZpbmQgc29tZSB0cmVuZHMgb2Ygd2hlbiB0aGluZ3MgYXJlIG1vcmUgbGlrZWx5IGNhbmNlbGVkLiAKCiMjIyAyLiBDcmVhdGUgc29tZSBleHBsb3JhdG9yeSBwbG90cyBvciB0YWJsZSBzdW1tYXJpZXMgb2YgdGhlIGRhdGEsIGNvbmNlbnRyYXRpbmcgbW9zdCBvbiByZWxhdGlvbnNoaXBzIHdpdGggdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiBLZWVwIGluIG1pbmQgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGlzIG51bWVyaWMsIDAgb3IgMS4gWW91IG1heSB3YW50IHRvIG1ha2UgaXQgY2F0ZWdvcmljYWwgKHlvdSBhbHNvIG1heSBub3QpLiBCZSBzdXJlIHRvIGFsc28gZXhhbWluZSBtaXNzaW5nIHZhbHVlcyBvciBvdGhlciBpbnRlcmVzdGluZyB2YWx1ZXMuCgpgYGB7cn0KaG90ZWxzICU+JQogIGdyb3VwX2J5KHByZXZpb3VzX2NhbmNlbGxhdGlvbnMpICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSkKYGBgCgpgYGB7cn0KaG90ZWxzICU+JQogIGFkZF9uX21pc3MoKSAlPiUKICBmaWx0ZXIobl9taXNzX2FsbCAhPSAwKSAlPiUKICBzZWxlY3QoY2hpbGRyZW4pCmBgYAoKYGBge3J9CmhvdGVscyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjdXN0b21lcl90eXBlLCBmaWxsID0gaG90ZWwpKSArCiAgZ2VvbV9iYXIoKQpgYGAKCiMjIyAzLiBNdXRhdGlvbnMuCgpgYGB7cn0KaG90ZWxzX21vZCA8LSBob3RlbHMgJT4lIAogIG11dGF0ZShpc19jYW5jZWxlZCA9IGFzLmZhY3Rvcihpc19jYW5jZWxlZCkpICU+JSAKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikpICU+JSAKICBzZWxlY3QoLWFycml2YWxfZGF0ZV95ZWFyLAogICAgICAgICAtcmVzZXJ2YXRpb25fc3RhdHVzLAogICAgICAgICAtcmVzZXJ2YXRpb25fc3RhdHVzX2RhdGUpICU+JSAKICBhZGRfbl9taXNzKCkgJT4lIAogIGZpbHRlcihuX21pc3NfYWxsID09IDApICU+JSAKICBzZWxlY3QoLW5fbWlzc19hbGwpCgpzZXQuc2VlZCg0OTQpCmBgYAoKYGBge3J9CmhvdGVsc19zcGxpdCA8LSBpbml0aWFsX3NwbGl0KGhvdGVsc19tb2QsIHByb3AgPSAuNSwgc3RyYXRhID0gJ2lzX2NhbmNlbGVkJykKYGBgCgpgYGB7cn0KaG90ZWxzX3RyYWluaW5nIDwtIHRyYWluaW5nKGhvdGVsc19zcGxpdCkKaG90ZWxzX3Rlc3RpbmcgPC0gdGVzdGluZyhob3RlbHNfc3BsaXQpCmBgYAoKIyMjIDQuIFByZS1wcm9jZXNzaW5nCgpgYGB7cn0KaG90ZWxzX3JlY2lwZSA8LSByZWNpcGUoaXNfY2FuY2VsZWQgfiAuLCAKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gaG90ZWxzX3RyYWluaW5nKSAlPiUKICBzdGVwX211dGF0ZShjaGlsZHJlbiA9IGFzLmZhY3RvcihpZmVsc2UoY2hpbGRyZW4gPT0gMCwwLDEpKSwKICAgICAgICAgICAgICBiYWJpZXMgPSBhcy5mYWN0b3IoaWZlbHNlKGJhYmllcyA9PSAwLDAsMSkpLAogICAgICAgICAgICAgIHByZXZpb3VzX2NhbmNlbGxhdGlvbnMgPSBhcy5mYWN0b3IoaWZlbHNlKHByZXZpb3VzX2NhbmNlbGxhdGlvbnMgPT0gMCwgMCwxKSkpICU+JQogIHN0ZXBfbXV0YXRlKGFnZW50ID0gYXMuZmFjdG9yKGlmZWxzZShhZ2VudCA9PSAnTlVMTCcsIDAsMSkpLAogICAgICAgICAgICAgIGNvbXBhbnkgPSBhcy5mYWN0b3IoaWZlbHNlKGNvbXBhbnkgPT0gJ05VTEwnLCAwLDEpKSkgJT4lCiAgc3RlcF9tdXRhdGUoY291bnRyeSA9IGZjdF9sdW1wX24oY291bnRyeSwgbiA9IDUpKSAlPiUKICBzdGVwX25vcm1hbGl6ZShhbGxfcHJlZGljdG9ycygpLC1hbGxfbm9taW5hbCgpKSAlPiUKICBzdGVwX2R1bW15KGFsbF9ub21pbmFsKCksIAogICAgICAgICAgICAgLWFsbF9vdXRjb21lcygpKQpgYGAKCiMjIyA1LiBSZWNpcGUgQnVpbGRpbmcuCgojIyMjIEluIGdlbmVyYWwsIHdoeSB3b3VsZCB3ZSB3YW50IHRvIHVzZSBMQVNTTyBpbnN0ZWFkIG9mIHJlZ3VsYXIgbG9naXN0aWMgcmVncmVzc2lvbj8gKEhJTlQ6IHRoaW5rIGFib3V0IHdoYXQgaGFwcGVucyB0byB0aGUgY29lZmZpY2llbnRzKS4KV2UgY2FuIGF2b2lkIG92ZXIgZml0dGluZyB0aGUgbW9kZWwgdG8gdGhlIHRlc3QgZGF0YSwgYnkgY29tcHJlc3NpbmcgYW5kIHVzaW5nIGxlc3MgdmFyaWFibGUgb3V0Y29tZXMuCgpgYGB7cn0KaG90ZWxzX3JlY2lwZSAlPiUKICBwcmVwKGhvdGVsc190cmFpbmluZykgJT4lCiAganVpY2UoKQpgYGAKCmBgYHtyfQpzZXQuc2VlZCg0OTQpCmhvdGVsX2N2IDwtIHZmb2xkX2N2KGhvdGVsc190cmFpbmluZywgdiA9IDUpCmBgYAoKYGBge3J9CmhvdGVsX2xhc3NvX21vZCA8LSAKICBsb2dpc3RpY19yZWcobWl4dHVyZSA9IDEpICU+JQogIHNldF9hcmdzKHBlbmFsdHkgPSB0dW5lKCkpICU+JSAKICBzZXRfZW5naW5lKCJnbG1uZXQiKSAlPiUgCiAgc2V0X21vZGUoImNsYXNzaWZpY2F0aW9uIikKYGBgCgpgYGB7cn0KaG90ZWxfbGFzc29fd2YgPC0KICB3b3JrZmxvdygpICU+JQogIGFkZF9yZWNpcGUoaG90ZWxzX3JlY2lwZSkgJT4lCiAgYWRkX21vZGVsKGhvdGVsX2xhc3NvX21vZCkKCmhvdGVsX2xhc3NvX3dmCmBgYAoKIyMjIDYuIFR1bmluZy4gCgpgYGB7cn0KcGVuYWx0eV9ncmlkIDwtIGdyaWRfcmVndWxhcihwZW5hbHR5KCksIGxldmVscyA9IDEwKQoKcGVuYWx0eV9ncmlkCmBgYAoKYGBge3J9CmhvdGVsX2xhc3NfdHVuZSA8LSBob3RlbF9sYXNzb193ZiAlPiUKICB0dW5lX2dyaWQocmVzYW1wbGVzID0gaG90ZWxfY3YsCiAgICAgICAgICAgIGdyaWQgPSBwZW5hbHR5X2dyaWQpCgpob3RlbF9sYXNzX3R1bmUKYGBgCgpgYGB7cn0KaG90ZWxfbGFzc190dW5lICU+JSAKICBjb2xsZWN0X21ldHJpY3MoKSAlPiUgCiAgZmlsdGVyKC5tZXRyaWMgPT0gImFjY3VyYWN5IikgJT4lIAogIGdncGxvdChhZXMoeCA9IHBlbmFsdHksIHkgPSBtZWFuKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfbG9nMTAoCiAgIGJyZWFrcyA9IHNjYWxlczo6dHJhbnNfYnJlYWtzKCJsb2cxMCIsIGZ1bmN0aW9uKHgpIDEwXngpLAogICBsYWJlbHMgPSBzY2FsZXM6OnRyYW5zX2Zvcm1hdCgibG9nMTAiLHNjYWxlczo6bWF0aF9mb3JtYXQoMTBeLngpKSkgKwogIGxhYnMoeCA9ICJwZW5hbHR5IiwgeSA9ICJhY2N1cmFjeSIpCmBgYAoKYGBge3J9CmhvdGVsX2xhc3NfdHVuZSAlPiUKICBzaG93X2Jlc3QobWV0cmljID0gJ2FjY3VyYWN5JykKCmhvdGVsX2xhc3NfdHVuZSAlPiUKICBzaG93X2Jlc3QoKQpgYGAKCgpgYGB7cn0KYmVzdF9wYXJhbSA8LSBob3RlbF9sYXNzX3R1bmUgJT4lIAogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCgpiZXN0X3BhcmFtCmBgYAoKYGBge3J9CmhvdGVsX2xhc3NvX2ZpbmFsX3dmIDwtIGhvdGVsX2xhc3NvX3dmICU+JSAKICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X3BhcmFtKQpob3RlbF9sYXNzb19maW5hbF93ZgpgYGAKCmBgYHtyfQpob3RlbF9sYXNzb19maW5hbF9tb2QgPC0gaG90ZWxfbGFzc29fZmluYWxfd2YgJT4lIAogIGZpdChkYXRhID0gaG90ZWxzX3RyYWluaW5nKQoKaG90ZWxfbGFzc29fZmluYWxfbW9kICU+JSAKICBwdWxsX3dvcmtmbG93X2ZpdCgpICU+JSAKICB0aWR5KCkgCmBgYAoKIyMjIyBBcmUgdGhlcmUgc29tZSB2YXJpYWJsZXMgd2l0aCBjb2VmZmljaWVudHMgb2YgMD8KWWVzLiAnYXJyaXZhbF9kYXRlX21vbnRoX1NlcHRlbWJlcicsICdhc3NpZ25lZF9yb29tX3R5cGVfTCcsICdtYXJrZXRfc2VnbWVudF9Hcm91cHMnLCAnbWFya2V0X3NlZ21lbnRfVW5kZWZpbmVkJywgJ2Rpc3RyaWJ1dGlvbl9jaGFubmVsX1VuZGVmaW5lZCcuCgojIyMgNy4gVGVzdGluZyBhbmQgRXZhbHVhdGlvbgoKYGBge3J9CmhvdGVsX2xhc3NvX2ZpbmFsX21vZCAlPiUgCiAgcHVsbF93b3JrZmxvd19maXQoKSAlPiUgCiAgdmlwKCkKYGBgCgojIyMjIFdoaWNoIHZhcmlhYmxlcyBzaG93IHVwIGFzIHRoZSBtb3N0IGltcG9ydGFudD8gQXJlIHlvdSBzdXJwcmlzZWQ/CidyZXNlcnZlZF9yb29tX3R5cGVfUCcsICdkZXBvc2l0X3R5cGVfTm9uLlJlZnVuZCcsICdhc3NpZ25lZF9yb29tX3R5cGVfbCcuICBXaHkgZG9lcyByb29tIHR5cGUgcmVzZXJ2ZWQgYW5kIGFzc2lnbmVkIG1hdHRlciBzbyBtdWNoPwoKYGBge3J9CmhvdGVsX2xhc3NvX3Rlc3QgPC0gaG90ZWxfbGFzc29fZmluYWxfd2YgJT4lIAogIGxhc3RfZml0KGhvdGVsc19zcGxpdCkKCmhvdGVsX2xhc3NvX3Rlc3QgJT4lIAogIGNvbGxlY3RfbWV0cmljcygpCmBgYAoKIyMjIyAgSG93IGRvIHRoZXkgY29tcGFyZSB0byB0aGUgY3Jvc3MtdmFsaWRhdGVkIG1ldHJpY3M/ClRlc3QgYWNjdXJhY3kgaXMgLjAwMiBoaWdoZXIgdGhhbiBteSBDViBtZXRyaWNzIG9uIGF2ZXJhZ2UuICBBbmQgJ3JvY19hdWMnIGlzIGFib3V0IC4wMDEgaGlnaGVyLgoKYGBge3J9CnByZWRzIDwtIGNvbGxlY3RfcHJlZGljdGlvbnMoaG90ZWxfbGFzc29fdGVzdCkKYGBgCgpgYGB7cn0KcHJlZHMgJT4lCiAgY29uZl9tYXQoaXNfY2FuY2VsZWQsIC5wcmVkX2NsYXNzKQpgYGAKCiMjIyMgV2hhdCBpcyB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIChzZW5zaXRpdml0eSk/IFdoYXQgaXMgdGhlIHRydWUgbmVnYXRpdmUgcmF0ZSAoc3BlY2lmaWNpdHkpPwpUcnVlIHBvc2l0aXZlIHJhdGUgPSA2NSUuClRydWUgbmVnYXRpdmUgcmF0ZSA9IDkxJS4KCmBgYHtyfQpwcmVkcyAlPiUKICBnZ3Bsb3QoYWVzKHg9LnByZWRfMSwgZmlsbCA9IGlzX2NhbmNlbGVkKSkgKyAKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAuNSwgY29sb3IgPSBOQSkKYGBgCgojIyMjIGEuIFdoYXQgd291bGQgdGhpcyBncmFwaCBsb29rIGxpa2UgZm9yIGEgbW9kZWwgd2l0aCBhbiBhY2N1cmFjeSB0aGF0IHdhcyBjbG9zZSB0byAxPyAKVGhlcmUgd291bGQgYmUgdmVyeSBoaWdoIGRlbnNpdHkgcGVha3MgYXQgMCBhbmQgMSwgYW5kIHZlcnkgbGl0dGxlIHRvIG5vIGRlbnNpdHkgaW4gdGhlIG1pZGRsZS4gCgojIyMjIGIuIE91ciBwcmVkaWN0aW9ucyBhcmUgY2xhc3NpZmllZCBhcyBjYW5jZWxlZCBpZiB0aGVpciBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgb2YgY2FuY2VsaW5nIGlzIGdyZWF0ZXIgdGhhbiAuNS4gSWYgd2Ugd2FudGVkIHRvIGhhdmUgYSBoaWdoIHRydWUgcG9zaXRpdmUgcmF0ZSwgc2hvdWxkIHdlIG1ha2UgdGhlIGN1dG9mZiBmb3IgcHJlZGljdGVkIGFzIGNhbmNlbGVkIGhpZ2hlciBvciBsb3dlciB0aGFuIC41PyAKSGlnaGVyIGN1dG9mZiB3b3VsZCBsZWFkIHRvIGEgaGlnaGVyIHRydWUgcG9zaXRpdmUgcmF0ZSwgYXMgdGhlIG1vZGVsIHdvdWxkIG9ubHkgcHJlZGljdCBjYW5jZWxsYXRpb25zIG9uIGNhc2VzIGl0IGlzIHZlcnkgc3VyZSBvZi4gCgojIyMjIGMuIFdoYXQgaGFwcGVucyB0byB0aGUgdHJ1ZSBuZWdhdGl2ZSByYXRlIGlmIHdlIHRyeSB0byBnZXQgYSBoaWdoZXIgdHJ1ZSBwb3NpdGl2ZSByYXRlPwpUaGUgdHJ1ZSBuZWdhdGl2ZSB3b3VsZCBkZWNyZWFzZS4gCgojIyMgOC4gTGV04oCZcyBzYXkgdGhhdCB0aGlzIG1vZGVsIGlzIGdvaW5nIHRvIGJlIGFwcGxpZWQgdG8gYm9va2luZ3MgMTQgZGF5cyBpbiBhZHZhbmNlIG9mIHRoZWlyIGFycml2YWwgYXQgZWFjaCBob3RlbCwgYW5kIHNvbWVvbmUgd2hvIHdvcmtzIGZvciB0aGUgaG90ZWwgd2lsbCBtYWtlIGEgcGhvbmUgY2FsbCB0byB0aGUgcGVyc29uIHdobyBtYWRlIHRoZSBib29raW5nLiBEdXJpbmcgdGhpcyBwaG9uZSBjYWxsLCB0aGV5IHdpbGwgdHJ5IHRvIGFzc3VyZSB0aGF0IHRoZSBwZXJzb24gd2lsbCBiZSBrZWVwaW5nIHRoZWlyIHJlc2VydmF0aW9uIG9yIHRoYXQgdGhleSB3aWxsIGJlIGNhbmNlbGluZyBpbiB3aGljaCBjYXNlIHRoZXkgY2FuIGRvIHRoYXQgbm93IGFuZCBzdGlsbCBoYXZlIHRpbWUgdG8gZmlsbCB0aGUgcm9vbS4gSG93IHNob3VsZCB0aGUgaG90ZWwgZ28gYWJvdXQgZGVjaWRpbmcgd2hvIHRvIGNhbGw/IEhvdyBjb3VsZCB0aGV5IG1lYXN1cmUgd2hldGhlciBpdCB3YXMgd29ydGggdGhlIGVmZm9ydCB0byBkbyB0aGUgY2FsbGluZz8gQ2FuIHlvdSB0aGluayBvZiBhbm90aGVyIHdheSB0aGV5IG1pZ2h0IHVzZSB0aGUgbW9kZWw/CklmIHRoZSBob3RlbCBpcyBnb2luZyB0byBjYWxsIHNvbWVvbmUsIHRoZSBtb2RlbCBzaG91bGQgYmUgb3ZlcndoZWxtaW5nbHkgY2VydGFpbiB0aGF0IHRoZSBwZXJzb24gaXMgbGlrZWx5IHRvIGNhbmNlbC4gIFRoZSBjdXRvZmYgd291bGQgaGF2ZSB0byBiZSBzdWNoIHRoYXQgdGhlIHRydWUgcG9zaXRpdmUgcmF0ZSBpcyB2ZXJ5IGhpZ2gsIHNvIHRoZSBob3RlbCBpc24ndCBjYWxsaW5nIHBlb3BsZSB3aG8gYXJlbid0IGxpa2VseSB0byBjYW5jZWwuICBNZWFzdXJlIHN1Y2Nlc3MgaWYgdGhlIHJvb21zIG9mIHRoZSBwZW9wbGUgd2hvIGNhbmNlbCBmcm9tIHRoYXQgY2FsbCBhcmUgZ2V0dGluZyBmaWxsZWQgd2l0aGluIHRoZSAxNCBkYXlzIGFmdGVyIHRoZSBjYWxsLiAgRGV0ZXJtaW5pbmcgd2hhdCB0cmF2ZWwgYWdlbnRzIGFuZCBjb21wYW5pZXMgaGF2ZSB0aGUgbW9zdCByZWxpYWJsZSBjdXN0b21lcnMsIGlmIHRoZSBjb250cmFjdHMgdGhleSBoYXZlIGFyZSB3b3J0aCB3aGlsZSBmb3IgdGhlbSB0byBrZWVwLgoKIyMjIDkuIEhvdyBtaWdodCB5b3UgZ28gYWJvdXQgcXVlc3Rpb25pbmcgYW5kIGV2YWx1YXRpbmcgdGhlIG1vZGVsIGluIHRlcm1zIG9mIGZhaXJuZXNzPyBBcmUgdGhlcmUgYW55IHF1ZXN0aW9ucyB5b3Ugd291bGQgbGlrZSB0byBhc2sgb2YgdGhlIHBlb3BsZSB3aG8gY29sbGVjdGVkIHRoZSBkYXRhPwpDb3VudHJ5IG9mIG9yaWdpbiBjb3VsZCBiZSBwcm9ibGVtYXRpYywgYXMgd2VsbCBkaXNjcmltaW5hdGlvbiBiYXNlZCBvbiBudW1iZXIgb2YgY2hpbGRyZW4gY291bGQgYmUgYW4gaXNzdWUuICBJIGFtIGFsc28gY29uZnVzZWQgb24gaG93IGNhbmNlbGxhdGlvbiBhbmQgcmVzZXJ2YXRpb24gZGF0YSB3YXMgY29sbGVjdGVkLiAgSWYgaXQgd2FzIGJhc2VkIG9uIHdlYnNpdGUgYm9va2luZyBvciB0cmF2ZWwgYWdlbnRzLCB0aGVuIHRob3NlIHBlb3BsZSB1c2luZyB0aG9zZSBzZXJ2aWNlcyB3b3VsZCBiZSB1bmZhaXJseSBkZWVtZWQgYXMgbGVzcyByZWxpYWJsZSBiZWNhdXNlIHRoZXkgaGF2ZSBhIHJlY29yZC4gIFdoZXJlYXMgcGVvcGxlIHdobyBkb24ndCBoYXZlIHRob3NlIG9ubGluZSByZWNvcmRzIHdvbid0IGhhdmUgdGhlIHNhbWUgaXNzdWVzLiAKCiMjIEJpYXMgYW5kIEZhaXJuZXNzCgojIyMjIERpZCB5b3UgaGVhciBhbnl0aGluZyB0aGF0IHN1cnByaXNlZCB5b3U/CldoZW4gcmVzcG9uZGluZyB0byB0aGUgZmlyc3QgcXVlc3Rpb24gZnJvbSB0aGUgYXVkaWVuY2UsIERyLiBUaG9tYXMgYnJpbmdzIHVwIHRoYXQgbWFjaGluZSBsZWFybmluZyBoYXMgb25seSBmb2N1c2VkIG9uIGhvdyB0byBvYnRhaW4gbGFyZ2UgYW1vdW50cyBvZiBkYXRhIGVmZmljaWVudGx5IHJhdGhlciB0aGFuIGxvb2tpbmcgYXQgaG93IHRoZSBkYXRhIGlzIG9idGFpbmVkIGFuZCB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIGl0c2VsZi4gIFRoaXMgd2FzIGFuIGludGVyZXN0aW5nIHBvaW50IEkgaGFkbid0IHRob3VnaHQgYWJvdXQgYmVmb3JlLCB0aGUgdG9waWMgb2YgZW5zdXJpbmcgdGVzdCBkYXRhIG1hdGNoZXMgdHJhaW5pbmcgZGF0YS4gIEkgZ3Vlc3MgSSBoYWRuJ3QgdGhvdWdodCBhYm91dCBpdCBiZWZvcmUgYmVjYXVzZSBJIHdvcmsgd2l0aCBkYXRhIHNldHMgZnJvbSBNTEIgdGhlIG1vc3QgYW5kIHRoZXJlIGlzbid0IHRoYXQgbXVjaCBkaWZmZXJlbmNlIHllYXIgdG8geWVhciBpbiBvdXRjb21lcy4gCgojIyMjIFdoeSBpcyBpdCBpbXBvcnRhbnQgdGhhdCB3ZSBwYXkgYXR0ZW50aW9uIHRvIGJpYXMgYW5kIGZhaXJuZXNzIHdoZW4gc3R1ZHlpbmcgZGF0YSBzY2llbmNlPwpJdCdzIGltcG9ydGFudCB0byBwYXkgYXR0ZW50aW9uIHRvIGJpYXMgYW5kIGZhaXJuZXNzIGJlY2F1c2UgdGhhdCB3b3JrIGRhdGEgc2NpZW50aXN0cyBkbyBjYW4gaGF2ZSBsYXJnZSBpbXBhY3RzIG9uIHBlb3BsZSdzIGxpdmVzLCBmb3IgZXhhbXBsZSBtb2RlbHMgdGhhdCBwcmVkaWN0IHJlY2lkaXZpc20gc2VuZCBjYW4ga2VlcCBwZW9wbGUgaW4gamFpbCBiYXNlZCBvbiBmYWN0b3JzIHRoYXQgaGF2ZSBub3RoaW5nIHRvIGRvIHdpdGggdGhlIGluZGl2aWR1YWwgZHVlIHRvIGxhcmdlIGFtb3VudHMgb2YgYmlhcyBidWlsdCBpbnRvIHRoZSBwcm9jZXNzLiBUaGUgb3V0Y29tZXMgb2YgdGhlc2UgbW9kZWxzIGNhbiBwcmV2ZW50IGZyZWVkb20gZnJvbSBwZW9wbGUuICBPbiBhIHNtYWxsZXIgc2NhbGUgd2UgaGF2ZSB0byBtYWtlIHN1cmUgb3VyIG1vZGVsIGRvZXMgd2hhdCB3ZSB3YW50IGl0IGRvLiAgV2Ugd2FudCB0aGUgbW9kZWwgdG8gYmUgZmFpciBhY3Jvc3MgYW5kIHBvaW50cyBpbiB0aGUgZGF0YSBzbyB3ZSBtdXN0IG1ha2Ugc3VyZSB0aGVyZSBpc24ndCBhIGxhcmdlIGJpYXMgdG93YXJkcyBhIHN1YnNlY3Rpb24gb2Ygb3VyIGRhdGEgc2V0LiAKCiMjIyMgSXMgdGhlcmUgYSB0eXBlIG9mIGJpYXMgRHIuIFRob21hcyBkaXNjdXNzZWQgdGhhdCB3YXMgbmV3IHRvIHlvdT8gQ2FuIHlvdSB0aGluayBhYm91dCBwbGFjZXMgeW91IGhhdmUgc2VlbiB0aGVzZSB0eXBlcyBvZiBiaWFzZXM/ClJlcHJlc2VudGF0aW9uIGJpYXMuICBJIHNhdyBhIGxvdCBvZiB0aGVzZSB0eXBlcyBvZiBiaWFzZXMgd2hlbiB3b3JraW5nIG92ZXIgbGFzdCBzdW1tZXIuICBJIHdhcyB3b3JraW5nIG9uIHByZWRpY3RpbmcgYWNjb3VudCB2aWFiaWxpdHksIHRoZSBkYXRhYmFzZSBoYWQgc29tZXRoaW5nIGxpa2UgMTAlIHRoYXQgd2VyZSBub3QgdmlhYmxlIGFjY291bnRzIHRoYXQgSSB3YXMgdHJ5aW5nIHRvIHByZWRpY3Qgd291bGQgbm90IGJlIHZpYWJsZS4gIEkgbWFkZSBhIG1vZGVsIGFuZCB3YXMgaW1wcmVzc2VkIHdpdGggbXlzZWxmIHRoYXQgSSBnb3QgaXQgdXAgdG8gOTAgc29tZSBvZGQgcGVyY2VudCBhY2N1cmFjeS4gIFVudGlsIEkgd2VudCBiYWNrIGFuZCBsb29rZWQgYXQgdGhlIGNvbmZ1c2lvbiBtYXRyaXggYW5kIHNhdyB0aGF0IGl0IHdhcyBwcmVkaWN0aW5nIGFsbW9zdCBhbGwgYWNjb3VudHMgdG8gYmUgdmlhYmxlLCBzbyBpdCB3YXMgcmVhbGx5IGJhZCBhdCBwcmVkaWN0aW5nIG5vbi12aWFibGUgYWNjb3VudHMuICBJIGV2ZW50dWFsbHkgZ290IGFyb3VuZCB0aGlzIGJ5IGNvbnN0cnVjdGluZyBhIGRhdGFzZXQgdGhhdCB3YXMgNTAtNTAgdmlhYmxlIHRvIG5vbi12aWFibGUsIGFuZCBxdWlja2x5IHNhdyB0aGF0IG15IG1vZGVsIGluIGFjdHVhbCB0ZXJtcyBzdWNrZWQuIAoKCgoKCgoKCgoKCgoKCgoKCgoKCg==